import { AxiosRequestConfig, AxiosPromise, AxiosResponse } from '../types'
import { parseHeaders } from '../helpers/headers'
import { createError } from '../helpers/error'

/**
 *  用于和服务器交互，可以在不刷新页面的情况下，更新局部数据
 * @author  姚格格
 * @date    2020-07-02 09:45
 */
export default function xhr(config: AxiosRequestConfig): AxiosPromise {
  return new Promise((resolve, reject) => {
    const { data = null, url, method = 'get', headers = {}, responseType, timeout } = config
    let request = new XMLHttpRequest()
    if (responseType) {
      request.responseType = responseType
    }
    // 处理超时错误
    if (timeout) {
      request.timeout = timeout
    }

    // 初始化一个请求 只能在js中使用
    request.open(method.toUpperCase(), url!, true)

    request.onreadystatechange = function handleLoad() {
      if (request.readyState !== 4) {
        return
      }
      // 非200的其他状态码
      if (request.status === 0) {
        return
      }

      // headers处理为一个对象来传参
      const responseHeaders = parseHeaders(request.getAllResponseHeaders())
      // 问题：这里娶不到data的返回值
      const responseData = responseType !== 'text' ? request.response : request.responseText
      // 实现AxiosResponse
      const response: AxiosResponse = {
        data: responseData,
        status: request.status,
        statusText: request.statusText,
        headers: responseHeaders,
        config,
        request
      }
      handleResponse(response)
    }
    // 处理网络错误：
    request.onerror = function handleError() {
      reject(createError('Network error', config, null, request))
    }
    // 处理超时错误
    request.ontimeout = function handleTimeout() {
      reject(createError(`timeout of ${timeout} ms exceeded`, config, 'ECONNABORTED', request))
    }

    // 设置 HTTP 请求头的值  (必须在 open() 之后、send() 之前调用 )
    Object.keys(headers).forEach(name => {
      if (data === null && name.toLowerCase() === 'content-type') {
        delete headers[name]
      } else {
        request.setRequestHeader(name, headers[name])
      }
    })

    // 发送请求，如果请求是异步的话，那么这个方法将在请求发送后立即返回
    request.send(data)

    function handleResponse(response: AxiosResponse): void {
      if (response.status >= 200 && response.status < 300) {
        resolve(response)
      } else {
        reject(
          createError(
            `Request failed with status code ${response.status}`,
            config,
            null,
            request,
            response
          )
        )
      }
    }
  })
}
